#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <memory>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include<functional>
#include "nocopy.h"

using namespace std;

using Func_t = function<string(const string&)>;

class Udp : public nocopy
{
public:
    Udp(const string &ip, const uint16_t &port,Func_t func=nullptr)
        : _ip(ip), _port(port),_Func(func)
    {
    }

    void Creat_socket()
    {
        // 1.创建套接字(创建文件细节)
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 创建套接字(域、套接字类型、协议)参数2基本可以固定tcp/udp
        if (_sockfd < 0)
        {
            cerr << "创建套接字失败" << endl;
            exit(-1);
        }
    }

    void Bind()
    {
        // 2.绑定(网络信息与文件信息相关联)
        struct sockaddr_in local;      //
        local.sin_family = AF_INET;    // 域，用于sockaddr类型接收时的辨别字段
        local.sin_port = htons(_port); // 端口号 主机转网络(用于发送)
        // local.sin_addr.s_addr = inet_addr(_ip.c_str()); // 四字节ip+转换成网络序列
        local.sin_addr.s_addr = INADDR_ANY; // 地址任意

        int n = bind(_sockfd, (sockaddr *)&local, sizeof(local));
        if (n != 0)
        {
            cerr << "套接字绑定失败" << endl;
        }
    }

    void Inet_info(const sockaddr_in &tmp)
    {
        string client_ip = inet_ntoa(tmp.sin_addr);
        uint16_t client_port = ntohs(tmp.sin_port);
        cout << " client_ip = " << client_ip << " client_port = " << to_string(client_port) << endl;
    }

    void Run_server()
    {
        char buffer[1024]; // 接收数据

        while (1) // 服务器端一直运行不退出
        {
            struct sockaddr_in foreign; // 客户端的套接字信息(输入输出型参数)
            socklen_t len = sizeof(foreign);

            size_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (sockaddr *)&foreign, &len);
            buffer[n] = 0; // 结尾

            // 获取客户端ip+port信息
            cout << "recive info: " << buffer;
            Inet_info(foreign);

            // 执行命令并返回执行结果
            string ret = _Func(buffer);
            sendto(_sockfd, ret.c_str(), ret.size(), 0, (sockaddr *)&foreign, len);
        }
    }

    void Run_client()
    {
        while (1)
        {
            // 发数据
            string tmp;
            cout << "please input:";
            getline(cin, tmp);
            struct sockaddr_in sv;
            sv.sin_family = AF_INET;                     // 域，用于sockaddr类型接收时的辨别字段
            sv.sin_port = htons(_port);                  // 端口号 主机转网络(用于发送)
            sv.sin_addr.s_addr = inet_addr(_ip.c_str()); // 四字节ip+转换成网络序列
            size_t n = sendto(_sockfd, tmp.c_str(), tmp.size(), 0, (sockaddr *)&sv, sizeof(sv));

            // 收数据
            struct sockaddr_in foreign; // 客户端的套接字信息(输入输出型参数)
            socklen_t len = sizeof(foreign);
            char buffer[1024]={0};
            recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (sockaddr *)&foreign, &len);
            cout<<buffer<<endl;//读取数据内容
        }
    }

    ~Udp()
    {
        close(_sockfd);
    }

private:
    string _ip;
    uint16_t _port;
    int _sockfd; // 套接字文件描述符
    Func_t _Func;
};